﻿// Copyright (c) 2014 Mike Fitzpatrick, CodeGenius.com

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace CodeGenius {
	/// <summary>
	/// 
	/// </summary>
	[Serializable]
	public partial class CICheckboxMenu : UserControl {

		static CICheckboxMenu _chkBoxMnu;

		/// <summary>
		/// 
		/// </summary>
		public interface IItem : IDropTarget, IComponent
		{
			string Name { get; set; }
			string Text {get; set; }
			string Tooltip { get; set; }
			object Value { get; set; }
			object Default { get; set; }
			ToolStripItem Item { get; set; }
			void Reset();
		}

#region "CItem"
		public abstract class CItem : ToolStripItem, IItem {
			protected internal string _name;
			protected internal string _text;
			protected internal string _tooltip;
			protected internal object _value;
			protected internal object _default;
			private ToolStripItem _item;

			protected CItem() 
			{
			}

			protected CItem(string text, object val, object defaultValue = null)
			{
				_text = text;
				_value = val;
				_default = defaultValue ?? val;
			}

			void IItem.Reset() {
				_value = _default;
			}

			public ToolStripItem Item
			{
				get { return _item; }
				set { 
					_item = value;
					_item.ToolTipText = _tooltip;
				}
			}

			string IItem.Tooltip {
				get { return _tooltip; }
				set { 
					_tooltip = value;
					if (Item != null)
						Item.ToolTipText = value;
				}
			}

			string IItem.Name {
				get { return _name; }
				set { _name = value; }
			}

			/// <summary>
			/// 
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Text")
			, Browsable(true)]
			string IItem.Text
			{
				set { _text = value; }
				get { return _text; }
			}

			/// <summary>
			/// 
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Value")
			, Browsable(true)]
			object IItem.Value
			{
				set { _value = value; }
				get { return _value; }
			}

			/// <summary>
			/// 
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Default Value")
			, Browsable(true)]
			object IItem.Default
			{
				set { _default = value; }
				get { return _default; }
			}
		}
#endregion "CItem"

#region "Textbox"
		/// <summary>
		/// 
		/// </summary>
		public class Textbox : CItem 
		{
			readonly ToolStripSeparator _sep;
			readonly ToolStripMenuItem _lbl;
			public Textbox(string text, string val, string defValue = null)
				: base(text, val, defValue) 
			{
				this.Item = new ToolStripTextBox {Text = val, AutoToolTip = false};
				((ToolStripTextBox)this.Item).BorderStyle = BorderStyle.FixedSingle;
				if (_chkBoxMnu.mnuMain.Items.Count > 0) {
					_sep = new ToolStripSeparator();
					_chkBoxMnu.mnuMain.Items.Add(_sep);
				}
				_lbl = new ToolStripMenuItem(text);
				_chkBoxMnu.mnuMain.Items.Add(_lbl);
				_chkBoxMnu.mnuMain.Items.Add(this.Item);
				_lbl.Click += FocusTxtboxEvent;
			}

			public string Tooltip 
			{
				set {
					Item.ToolTipText = value;
					ToolTipText = value;
					_lbl.ToolTipText = value;
				}
				get {
					return _tooltip;
				}
			}

			public static implicit operator ToolStripTextBox(Textbox txtbox) {
				return (ToolStripTextBox)(txtbox.Item);
			}

			void FocusTxtboxEvent(object sender, EventArgs e) {
				((ToolStripTextBox)Item).Focus();
			}

			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu value")
			, Browsable(true)]
			public string Value { get; set; }

			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Default")
			, Browsable(true)]
			public string Default { get; set; }
		}
#endregion "Textbox"

#region "Label"
		/// <summary>
		/// 
		/// </summary>
		public class Label : CItem 
		{
			readonly ToolStripSeparator _tsSep = null;
			public Label(string text, bool hasSeperator = false)
				: base(text, null, null) {
				if (hasSeperator) {
					_tsSep = new ToolStripSeparator();
					_chkBoxMnu.mnuMain.Items.Add(_tsSep);
				}
				Item = new ToolStripMenuItem {AutoToolTip = false, Text = text};
				((ToolStripMenuItem)Item).Checked = false;
				((ToolStripMenuItem)Item).CheckOnClick = false;
				_chkBoxMnu.mnuMain.Items.Add(Item);
			}

			public static implicit operator ToolStripMenuItem(Label lbl) {
				return (ToolStripMenuItem)(lbl.Item);
			}
		}
#endregion "Label"

#region "Checkbox"
		/// <summary>
		/// 
		/// </summary>
		public class Checkbox : CItem
		{
			readonly ToolStripSeparator _tsSep = null;
			public Checkbox(string text, bool val, bool hasSeperator = false, bool? defValue = null)
				: base(text, val, defValue) 
			{
				if (hasSeperator) {
					_tsSep = new ToolStripSeparator();
					_chkBoxMnu.mnuMain.Items.Add(_tsSep);
				}
				Item = new ToolStripMenuItem {AutoToolTip = false, Text = text};
				((ToolStripMenuItem)Item).Checked = val;
				((ToolStripMenuItem)Item).CheckOnClick = true;
				_chkBoxMnu.mnuMain.Items.Add(Item);
			}

			public static implicit operator ToolStripMenuItem(Checkbox chkbox) {
				return (ToolStripMenuItem)(chkbox.Item);
			}

			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Value")
			, Browsable(true)]
			public bool Value { get; set; }

			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
			, Description("Menu Default")
			, Browsable(true)]
			public bool Default { get; set; }
		}
#endregion "Checkbox"

		static int _nestLevel = 0;
		readonly ToolStripMenuItem _activator = null;
		/// <summary>
		/// 
		/// </summary>
		/// <param name="activator"></param>
		public CICheckboxMenu(ToolStripMenuItem activator = null) 
		{
			InitializeComponent();
			_activator = activator;
			Items = new Dictionary<string, IItem>();
			_chkBoxMnu = this;
			if (activator == null) {
				chkMenu.CheckedChanged += ToggleMenuEvent;
			} else {
				activator.BackColor = SystemColors.Info;
				activator.Click += ToggleMenuEvent;
			}
			mnuMain.ShowCheckMargin = true;
			mnuMain.ShowImageMargin = false;
			mnuMain.Tag = _nestLevel;
			_nestLevel++;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void CICheckboxMenu_Load(object sender, EventArgs e) {
			if (Parent != null) {
				Parent.LostFocus += ParentLostFocus;
				Parent.Move += ParentLostFocus;
				Parent.Leave += ParentLostFocus;
			}
			Visible = true;
		}

		private bool _keepMnuMain;
		private bool _chkMenuInHere = false;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ToggleMenuEvent(object sender, EventArgs e) 
		{
			if (_chkMenuInHere)
				return;
			_chkMenuInHere = true;
			Point pt;
			bool isActive;
			if (_activator != null) {
				_activator.Tag = !(bool)_activator.Tag;
				isActive = (bool)_activator.Tag;
				pt = new Point(0, 0); 
			} else {
				isActive = chkMenu.Checked;
				switch (DefaultDropDownDirection) {
				case ToolStripDropDownDirection.BelowRight:
					pt = new Point(0 //chkMenu.Width - mnuMain.DisplayRectangle.Width
						, chkMenu.Height);
					break;
				case ToolStripDropDownDirection.AboveRight:
					pt = new Point(0 //chkMenu.Width - mnuMain.DisplayRectangle.Width
						, chkMenu.Height);
					break;
				default:
					pt = new Point(chkMenu.Width - mnuMain.DisplayRectangle.Width
						, chkMenu.Height);
					break;
				}
			}
			if (isActive) {
				_keepMnuMain = true;
				mnuMain.SuspendLayout();
				mnuMain.Visible = true;
				mnuMain.AutoSize = true;
				mnuMain.ResumeLayout(true);
				for (int i = 0; i < 10; i++) {
					if (sender is ToolStripMenuItem) {
						mnuMain.Show();
					} else {
						mnuMain.Show(chkMenu, pt, DefaultDropDownDirection);
					}
					Application.DoEvents();
					if (mnuMain.Visible)
						break;
					Thread.Sleep(100);
				}
			} else {
				MnuMainClose();
			}
			_chkMenuInHere = false;
			//Application.DoEvents();
			//Thread.Sleep(100);
		}

		private void MnuMainClose() {
			_keepMnuMain = false;
			mnuMain.Hide();
			mnuMain.Visible = false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mnuMain_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
			e.Cancel = _keepMnuMain;
			Debug.WriteLine("mnuMain= " + mnuMain.Tag + ", Cancel = " + _keepMnuMain + ", e=" + e.CloseReason);
			Application.DoEvents();
			Thread.Sleep(0);
		}

		/// <summary>
		/// 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
		, Description("Check Button Text")
		, Browsable(true)]
		public new string Text {
			get { 
				if (_activator == null)
					return chkMenu.Text;
				return _activator.Text;
			}
			set {
				if (_activator == null)
					chkMenu.Text = value;
				else
					_activator.Text = value;  
			}
		}

		/// <summary>
		/// 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
		, Description("Menu")
		, Browsable(true)]
		public ContextMenuStrip Menu {
			get { return mnuMain; }
		}

		public IItem this[int index] {
			get { return Items.Values.ElementAt(index); }
		}

		public IItem this[string name] {
			get { return Items[name]; }
		}
		
		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <param name="lbl"></param>
		/// <param name="chkBoxMnu"></param>
		/// <returns></returns>
		public CICheckboxMenu Add(string name, Label lbl, CICheckboxMenu chkBoxMnu) 
		{
			if (chkBoxMnu == null) {
				chkBoxMnu = new CICheckboxMenu((ToolStripMenuItem)lbl.Item);
			}
			((ToolStripMenuItem)lbl).DropDown = chkBoxMnu.mnuMain;
			((ToolStripMenuItem)lbl).Click += LblToggleDropDownEvent;
			((ToolStripMenuItem)lbl).Tag = false;
			return chkBoxMnu;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void LblToggleDropDownEvent(object sender, EventArgs e) {
			ToolStripMenuItem lbl = (ToolStripMenuItem)sender;
			if (lbl.DropDown.Visible)
				lbl.DropDown.Close();
			else
				lbl.DropDown.Show();
			Application.DoEvents();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <param name="lbl"></param>
		/// <param name="mnu"></param>
		/// <returns></returns>
		public ContextMenuStrip Add(string name, Label lbl, ContextMenuStrip mnu) {
			if (mnu == null) {
				mnu = new ContextMenuStrip();
			}
			((ToolStripMenuItem)lbl.Item).DropDown = mnu;
			mnu.Name = name;
			return mnu;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public IItem Add(string name, IItem item) {
			Items.Add(name, item);
			item.Name = name;
			return item;
		}

		/// <summary>
		/// 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
		, Description("Menu Items")
		, Browsable(true)]
		public Dictionary<string, IItem> Items { get; set; }

		void ParentLostFocus(object sender, EventArgs e) {
			if (mnuMain.Visible)
				chkMenu.Checked = false;
			MnuMainClose();
		}

		/// <summary>
		/// 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
		, Description("Menu DropDownDirection")
		, Browsable(true)]
		public ToolStripDropDownDirection DefaultDropDownDirection {
			set { mnuMain.DefaultDropDownDirection = value; }
			get { return mnuMain.DefaultDropDownDirection; }
		}

		private bool _visible = true;
		/// <summary>
		/// 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
		, Description("Menu Visible")
		, Browsable(true)]
		public new bool Visible {
			set {
				_visible = value;
				base.Visible = _visible;
			}
			get {
				return _visible;
			}
		}

	} // class CICheckboxMenu

	[Serializable]
	public class CICheckboxMenuCollection {
		public List<CICheckboxMenu.IItem> Items {
			get;
			set;
		}
		public CICheckboxMenuCollection() {
			Items = new List<CICheckboxMenu.IItem>();
		}
	}

}
